package edu.friday.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import edu.friday.common.result.RestResult;
import edu.friday.common.constant.Constants;
import edu.friday.common.constant.GenConstants;
import edu.friday.common.exception.CustomException;
import edu.friday.common.result.TableDataInfo;
import edu.friday.repository.GenTableColumnRepository;
import edu.friday.repository.GenTableRepository;
import edu.friday.model.GenTable;
import edu.friday.model.GenTableColumn;
import edu.friday.model.dto.GenTableColumnDTO;
import edu.friday.model.dto.GenTableDTO;
import edu.friday.model.vo.GenTableColumnVO;
import edu.friday.model.vo.GenTableVO;
import edu.friday.service.GenTableService;
import edu.friday.utils.BeanUtils;
import edu.friday.utils.GenUtils;
import edu.friday.utils.StringUtils;
import edu.friday.utils.gen.VelocityInitializer;
import edu.friday.utils.gen.VelocityUtils;
import edu.friday.utils.security.SecurityUtils;
import org.apache.commons.io.IOUtils;
import org.apache.velocity.Template;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.Velocity;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Example;
import org.springframework.data.domain.ExampleMatcher;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.StringWriter;
import java.util.*;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

/**
 * 业务 服务层实现
 *
 */
@Service
public class GenTableServiceImpl implements GenTableService {
    private static final Logger log = LoggerFactory.getLogger(GenTableServiceImpl.class);

    @Autowired
    private GenTableRepository genTableRepository;

    @Autowired
    private GenTableColumnRepository genTableColumnRepository;

    /**
     * 查询业务信息
     *
     * @param id 业务ID
     * @return 业务信息
     */
    @Override
    public RestResult selectGenTableById(Long id) {
        Map<String, Object> map = new HashMap<String, Object>();
        GenTableVO rs = new GenTableVO();
        GenTable genTable = genTableRepository.getOne(id);
        if (null == genTable) {
            return RestResult.error();
        }
        // TODO get columwn list by table id
        GenTableColumn genTableColumn = new GenTableColumn(id);
        Example<GenTableColumn> example = Example.of(genTableColumn);
        List<GenTableColumn> list = genTableColumnRepository.findAll(example);
        List<GenTableColumnVO> columns = BeanUtils.copyProperties(list, GenTableColumnVO.class);
        //
        BeanUtils.copyProperties(genTable, rs);
        rs.setColumns(columns);
        setTableFromOptions(rs);
        //
        map.put("info" , rs);
        map.put("rows" , columns);
        return RestResult.success(map);
    }

    /**
     * 查询业务列表
     *
     * @param genTable 业务信息
     * @return 业务集合
     */
    @Override
    public TableDataInfo selectGenTableList(GenTableVO genTable, Pageable page) {
        GenTable genTableTemp = new GenTable();
        genTableTemp.setTableName(genTable.getTableName());
        genTableTemp.setTableComment(genTable.getTableComment());
        ExampleMatcher exampleMatcher = ExampleMatcher.matching()
                .withMatcher("tableName" , ExampleMatcher.GenericPropertyMatchers.contains())
                .withMatcher("tableComment" , ExampleMatcher.GenericPropertyMatchers.contains());
        Example<GenTable> example = Example.of(genTableTemp, exampleMatcher);
        Page<GenTable> queryPage = genTableRepository.findAll(example, page);
        List<GenTableVO> rs = BeanUtils.copyProperties(queryPage.toList(), GenTableVO.class);
        return TableDataInfo.success(rs, queryPage.getTotalElements());
    }


    /**
     * 查询据库列表
     *
     * @param genTable 业务信息
     * @return 数据库表集合
     */
    public TableDataInfo selectDbTableList(GenTableVO genTable, Pageable page) {
        GenTable params = new GenTable();
        BeanUtils.copyPropertiesIgnoreEmpty(genTable, params);
        List<GenTableDTO> list = genTableRepository.selectDbTableList(params, page);
        return TableDataInfo.success(list, genTableRepository.countDbTableList(params));
    }

    /**
     * 查询据库列表
     *
     * @param tableNames 表名称组
     * @return 数据库表集合
     */
    public List<GenTableVO> selectDbTableListByNames(String[] tableNames) {
        List<GenTableDTO> list = genTableRepository.selectDbTableListByNames(tableNames);
        List<GenTableVO> rs = BeanUtils.copyProperties(list, GenTableVO.class);
        return rs;
    }

    /**
     * 修改业务
     *
     * @param genTable 业务信息
     * @return 结果
     */
    @Override
    @Transactional
    public void updateGenTable(GenTableVO genTable) {
        String options = JSON.toJSONString(genTable.getParams());
        genTable.setOptions(options);
        GenTable params = new GenTable();
        BeanUtils.copyPropertiesIgnoreEmpty(genTable, params);
        genTableRepository.saveAndFlush(params);
        GenTableColumn genTableColumn;
        for (GenTableColumnVO genTableColumnVO : genTable.getColumns()) {
            genTableColumn = new GenTableColumn();
            BeanUtils.copyProperties(genTableColumnVO, genTableColumn);
            genTableColumnRepository.saveAndFlush(genTableColumn);
        }
    }

    /**
     * 删除业务对象
     *
     * @param tableIds 需要删除的数据ID
     * @return 结果
     */
    @Override
    @Transactional
    public void deleteGenTableByIds(Long[] tableIds) {
        genTableRepository.deleteByIds(tableIds);
        genTableColumnRepository.deleteByIds(tableIds);
    }

    /**
     * 导入表结构
     *
     * @param tableList 导入表列表
     */
    @Override
    @Transactional
    public void importGenTable(List<GenTableVO> tableList) {
        String operName = SecurityUtils.getUsername();
        for (GenTableVO table : tableList) {
            try {
                String tableName = table.getTableName();
                GenUtils.initTable(table, operName);
                int row = 0;
                GenTable genTable = new GenTable();
                BeanUtils.copyProperties(table, genTable);
                genTableRepository.saveAndFlush(genTable);
                if (null != genTable.getTableId() && !genTable.getTableId().equals(0)) {
                    // 保存列信息
                    List<GenTableColumnDTO> genTableColumns = genTableColumnRepository.selectDbTableColumnsByName(tableName);
                    for (GenTableColumnDTO column : genTableColumns) {
                        GenTableColumnVO columnVO = new GenTableColumnVO();
                        BeanUtils.copyProperties(column, columnVO);
                        GenUtils.initColumnField(columnVO, table);
                        GenTableColumn columnData = new GenTableColumn();
                        BeanUtils.copyProperties(columnVO, columnData);
                        columnData.setTableId(genTable.getTableId());
                        genTableColumnRepository.saveAndFlush(columnData);
                    }
                }
            } catch (Exception e) {
                log.error("表名 " + table.getTableName() + " 导入失败：" , e);
            }
        }
    }

    /**
     * 预览代码
     *
     * @param tableId 表编号
     * @return 预览数据列表
     */
    public RestResult previewCode(Long tableId) {
        Map<String, String> dataMap = new LinkedHashMap<>();
        // 查询表信息
        GenTable genTable = genTableRepository.getOne(tableId);
        if (null == genTable) {
            throw new CustomException("选定的表不存在");
        }
        // 查询列信息
        GenTableColumn genTableColumn = new GenTableColumn(tableId);
        Example<GenTableColumn> example = Example.of(genTableColumn);
        List<GenTableColumn> list = genTableColumnRepository.findAll(example);
        GenTableVO table = new GenTableVO();
        BeanUtils.copyProperties(genTable, table);
        List<GenTableColumnVO> columns = BeanUtils.copyProperties(list, GenTableColumnVO.class);
        table.setColumns(columns);
        setPkColumn(table, columns);
        VelocityInitializer.initVelocity();
        VelocityContext context = VelocityUtils.prepareContext(table);

        // 获取模板列表
        List<String> templates = VelocityUtils.getTemplateList(table.getTplCategory());
        for (String template : templates) {
            // 渲染模板
            StringWriter sw = new StringWriter();
            Template tpl = Velocity.getTemplate(template, Constants.UTF8);
            tpl.merge(context, sw);
            dataMap.put(template, sw.toString());
        }
        return RestResult.success(dataMap);
    }

    /**
     * 生成代码
     *
     * @param tableName 表名称
     * @return 数据
     */
    @Override
    public byte[] generatorCode(String tableName) {
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        ZipOutputStream zip = new ZipOutputStream(outputStream);
        generatorCode(tableName, zip);
        IOUtils.closeQuietly(zip);
        return outputStream.toByteArray();
    }

    /**
     * 批量生成代码
     *
     * @param tableNames 表数组
     * @return 数据
     */
    @Override
    public byte[] generatorCode(String[] tableNames) {
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        ZipOutputStream zip = new ZipOutputStream(outputStream);
        for (String tableName : tableNames) {
            generatorCode(tableName, zip);
        }
        IOUtils.closeQuietly(zip);
        return outputStream.toByteArray();
    }

    /**
     * 查询表信息并生成代码
     */
    private void generatorCode(String tableName, ZipOutputStream zip) {
        // 查询表信息
        GenTable table = new GenTable();
        table.setTableName(tableName);
        Example<GenTable> example = Example.of(table);
        Optional<GenTable> op = genTableRepository.findOne(example);
        table = op.get();
        // 查询列信息
        GenTableColumn genTableColumn = new GenTableColumn();
        genTableColumn.setTableId(table.getTableId());
        Example<GenTableColumn> colExample = Example.of(genTableColumn);
        List<GenTableColumn> list = genTableColumnRepository.findAll(colExample);
        //
        GenTableVO genTableVO = new GenTableVO();
        BeanUtils.copyProperties(table, genTableVO);
        List<GenTableColumnVO> columns = BeanUtils.copyProperties(list, GenTableColumnVO.class);
        genTableVO.setColumns(columns);
        setPkColumn(genTableVO, columns);
        //
        VelocityInitializer.initVelocity();
        VelocityContext context = VelocityUtils.prepareContext(genTableVO);
        // 获取模板列表
        List<String> templates = VelocityUtils.getTemplateList(table.getTplCategory());
        for (String template : templates) {
            // 渲染模板
            StringWriter sw = new StringWriter();
            Template tpl = Velocity.getTemplate(template, Constants.UTF8);
            tpl.merge(context, sw);
            try {
                // 添加到zip
                zip.putNextEntry(new ZipEntry(VelocityUtils.getFileName(template, table)));
                IOUtils.write(sw.toString(), zip, Constants.UTF8);
                IOUtils.closeQuietly(sw);
                zip.flush();
                zip.closeEntry();
            } catch (IOException e) {
                log.error("渲染模板失败，表名：" + table.getTableName(), e);
            }
        }
    }

    /**
     * 修改保存参数校验
     *
     * @param genTable 业务信息
     */
    public void validateEdit(GenTableVO genTable) {
        if (GenConstants.TPL_TREE.equals(genTable.getTplCategory())) {
            String options = JSON.toJSONString(genTable.getParams());
            JSONObject paramsObj = JSONObject.parseObject(options);
            if (StringUtils.isEmpty(paramsObj.getString(GenConstants.TREE_CODE))) {
                throw new CustomException("树编码字段不能为空");
            } else if (StringUtils.isEmpty(paramsObj.getString(GenConstants.TREE_PARENT_CODE))) {
                throw new CustomException("树父编码字段不能为空");
            } else if (StringUtils.isEmpty(paramsObj.getString(GenConstants.TREE_NAME))) {
                throw new CustomException("树名称字段不能为空");
            }
        }
    }

    /**
     * 设置主键列信息
     *
     * @param table   业务表信息
     * @param columns 业务字段列表
     */
    public void setPkColumn(GenTableVO table, List<GenTableColumnVO> columns) {
        for (GenTableColumnVO column : columns) {
            if (column.isPk()) {
                table.setPkColumn(column);
                break;
            }
        }
        if (StringUtils.isNull(table.getPkColumn())) {
            table.setPkColumn(columns.get(0));
        }
    }

    /**
     * 设置代码生成其他选项值
     *
     * @param genTable 设置后的生成对象
     */
    public void setTableFromOptions(GenTableVO genTable) {
        JSONObject paramsObj = JSONObject.parseObject(genTable.getOptions());
        if (StringUtils.isNotNull(paramsObj)) {
            String treeCode = paramsObj.getString(GenConstants.TREE_CODE);
            String treeParentCode = paramsObj.getString(GenConstants.TREE_PARENT_CODE);
            String treeName = paramsObj.getString(GenConstants.TREE_NAME);
            genTable.setTreeCode(treeCode);
            genTable.setTreeParentCode(treeParentCode);
            genTable.setTreeName(treeName);
        }
    }

}